package org.sample.springbootreactive.web;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.sample.springbootcommon.model.DateTimeRequest;
import org.sample.springbootcommon.model.DateTimeResponse;
import org.sample.springbootreactive.service.DateTimeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@EnableAutoConfiguration
@RequestMapping("/task")
public class ReactiveTaskController {

	private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveTaskController.class);

	@Autowired
	private DateTimeService taskService;

	@GetMapping("/run")
	public List<String> run(@RequestParam int count) {

		Date startTime = new Date();

		List<Mono<DateTimeResponse>> monos = new ArrayList<>(count);
		for (int i = 0; i < count; i++) {
			DateTimeRequest request = new DateTimeRequest();
			request.setId(Integer.toString(i));
			Mono<DateTimeResponse> mono = taskService.post(request);
			monos.add(mono);
		}

		Mono<List<String>> zippedMono = Mono.zip(monos, objects -> {
			int objCount = objects.length;
			List<String> results = new ArrayList<>(objCount);
			for (Object object : objects) {
				DateTimeResponse response = (DateTimeResponse) object;
				String result = String.format("%2s: %s - %s", response.getId(), response.getStart(), response.getEnd());
				results.add(result);
			}
			return results;
		});

		List<String> results = zippedMono.block();

		Date endTime = new Date();
		LOGGER.info("Elapsed time: {}s", (endTime.getTime() - startTime.getTime()) / 1000);

		return results;
	}
	
	@GetMapping("/collect")
	public Mono<List<String>> collect(@RequestParam int count) {
		Flux<String> responseFlux = Flux.range(0, count).map(
				id -> {
					DateTimeRequest request = new DateTimeRequest();
					request.setId(Integer.toString(id));
					return request;
				}
			).flatMap(taskService::post).map(
				response -> {
					String result = String.format("%2s: %s - %s", response.getId(), response.getStart(), response.getEnd());
					return result;
				}
			);
		
		final Date startTime = new Date();
		responseFlux = responseFlux.doOnComplete(() -> {
			Date endTime = new Date();
			LOGGER.info("Elapsed time: {}s", (endTime.getTime() - startTime.getTime()) / 1000);
		});
		
		return responseFlux.collectList();
	}

}
